#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<sys/types.h>

#define NUM 1024
#define SIZE 32
//保存完整的命令字符串
char cmd_line[NUM];
//保存打散之后的命令行字符串
char *g_argv[SIZE];

//写一个环境变量的bufer，用来存储临时的环境变量，防止cmd_line被清空
char my_num[64];

//定义标志位的含义
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
#define NONE_REDIR 0
int redir_status = NONE_REDIR;

char *CheckRedir(char *start)
{
  assert(start);
  char *end = start + strlen(start) - 1;// ls -a -l
  
  //从后往前找
  while(end >= start)
  {
    if(*end == '>')
    {
      if(*(end - 1) == '>')
      {
        redir_status = APPEND_REDIR; 
        *(end - 1) = '\0';
        end++;  //重定向后字符串的首地址
        break;
      }
      redir_status = OUTPUT_REDIR;
      *end = '\0';
      end++;   // ls -a>myfile.txt   ->ls -a\0myflie.txt
      break;
    }
    else if(*end == '<')
    {
      redir_status = INPUT_REDIR;
      *end = '\0';
      end++;
      break;
    }
    else{
      end--;
    }
  }
  if(end>=start)
  {
    return end; //要打开的文件名
  }
  else{
    return NULL;  //没有重定向功能
  }
} 

//shell运行原理：通过让子进程执行命令，父进程等待&&解析命令
int main()
{
  //0.命令行解释器一定是一个常驻内存的进程，不退出
  while(1)
  {
    //1.打印出提示信息[]
    printf("[xty@localhost myshell]#] ");
    fflush(stdout);

    //2.获取用户键盘的输入[输入的是各种指令和选项:"ls -a -l -i"]
    memset(cmd_line, '\0', sizeof(cmd_line));
    if(fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
    {
      continue;
    }
    if(cmd_line[0] == '\n')
    {
      continue;
    }
    cmd_line[strlen(cmd_line)-1] = '\0';
    //发现有"ls -a -l\n'\o'" 回车换行，我们应该删除这个
    //printf("echo:%s \n", cmd_line);
    

    //在分析指令之前就检查有没有重定向
    char* sep = CheckRedir(cmd_line);
    
    //3. 将命令行选项使用strtok分开, "ls -a -l"-> "ls", "-a", "-l", "NULL"
    g_argv[0] = strtok(cmd_line, " ");
    int index = 1;

    if(strcmp(g_argv[0], "ls")==0)
    {
      g_argv[index++]="--color=auto";
    }
    //还可以让编译器支"ll"
    if(strcmp(g_argv[0],"ll")==0)
    {
      g_argv[0] = "ls";
      g_argv[index++] = "-l";
      g_argv[index++] = "--color=auto";
    }

    //让ls命令有颜色
    while(g_argv[index++] = strtok(NULL, " "));
    
    ////检查一下g_argv对不对
    //for(index = 0; g_argv[index];index++)
    //{
    //  //虽然存入的是地址，但是%s，会将它看成字符串打印出来
    //  printf("g_argv[%d] = %s\n", index, g_argv[index]);
    //}


    // export MYNUM=111222333
    if(strcmp(g_argv[0], "export")==0 && g_argv[1]!=NULL)
    {
      strcpy(my_num, g_argv[1]);
      int ret = putenv(my_num);
      if (ret == 0) printf("%s export sucess\n", my_num);  //查看一下导入的是什么
      continue;
    }



    //让父进程执行cd 命令
    if(strcmp(g_argv[0], "cd")==0)
    {
      if(g_argv[1]!=NULL) chdir(g_argv[1]); // cd .., cd path
      continue;
    }
    //4.让子进程执行命令，执行完后给父进程返回值
    pid_t id = fork();
    if(id==0)
    {
      //子进程,执行命令 
      printf("子进程开始执行任务\n");
      if(sep!=NULL)
      {
        int fd = -1;
        //有重定向
        switch(redir_status)
        {
          case INPUT_REDIR:
            fd = open(sep, O_RDONLY);
            dup2(fd, 0);
            break;
          case OUTPUT_REDIR:
            fd = open(sep, O_WRONLY | O_TRUNC | O_CREAT, 0666);
            dup2(fd, 1);
            break;
          case APPEND_REDIR:
            fd = open(sep, O_WRONLY | O_APPEND | O_CREAT, 0666);
            dup2(fd, 1);
            break;
          default:
            printf("erro????????\n");
            break;
        }
      }
      
      execvp(g_argv[0], g_argv);
      exit(1);
    }
   
    //father
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret>0) printf("exit code:%d\n", WEXITSTATUS(status));
  }
  return 0;
}
